﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;

using Sampler;
using Shapes.Misc;
using Shapes.Geometry;
using Shapes;
using Shapes.Misc.Appearance;
using ShapesExtensions;


namespace TestGame.Samples
{
    /// <summary>
    /// This sample shows how to modify a LineStrip.
    /// The user is able to add, remove or move a point.
    /// To get a better feedback the nearest corner to the mousecurser is highlighted.
    /// To see, which line will be splitted when adding a point, 
    /// the nearest point on the nearest line is highlighted as well.
    /// It is also possible to make a Polygon from the LineStrip.
    /// </summary>
    public class LineStripEditingSample : Sample
    {
        #region Fields

        // a class which allows to draw a shape into a texture
        TextureGenerator _Generator;

        // The LineStrip
        LineStrip _Strip;
        // The Polygon made from the LineStrip (later by pressing C)
        Polygon _StripPoly = null;
        // The Textur which displays the LineStrip / Polygon
        Texture2D _StripTexture;

        // A Dot at the nearest corner
        Dot _CurrentVertex;
        // The Texture of the Dot. It is used 3 times (-> Draw())
        Texture2D _VertexTexture;
        // The Position of the nearest Point on the Line and the active corner (while moving)
        Vector2 _LinePoint, _MoveVertex;


        TestGame.HelperClasses.CursorHelper _Cursor;
        // A Shape for the mouse cursor
        Polygon _CursorShape;
        // the Mouse Cursor's Texture
        Texture2D _CursorTexture;

        // the brush for the LineStrip and Dot textures
        Brush _Brush;


        // some bools to check which buttons are currently pressed
        bool _IsMoveButtonDown;

        // if true, a nice texture will be generated everytime the geometry changes
        bool _DisplayGeneratedTexture;

        #endregion

        public LineStripEditingSample(TextureGenerator textureGenerator)
            : base("LineStrip Editing Sample:\n"
            + "Create a custom form.\n\n"
            + "Left click, press A-Button or Enter to add a point.\n"
            + "Right click, press B-Button or Right Shift to remove a point.\n"
            + "Hold middle mouse button, X-Button or Space to move a point.\n\n"
            + "Press C or Y-Button to make a Polygon, and C or Y-Button again to reset.\n"
            + "Press D or RT to switch between primitive and generated display mode")
        {
            _Generator = textureGenerator;

            _Cursor = new TestGame.HelperClasses.CursorHelper(Game.GraphicsDevice);
            _Cursor.ButtonPressed += _Cursor_ButtonPressed;
            _Cursor.ButtonReleased += _Cursor_ButtonReleased;

        }

        #region Initialize
        protected override void Initialize()
        {
            // the brush has a little blurred outline
            _Brush = new Brush(3);
            _Brush.AddMask(new BrushMaskFallOff(2));

            // Details in the 'other methods' region
            CreateInitialStrip();
            MakeTexture();
            // ...

            // Create the Dot Shape
            _CurrentVertex = new Dot(_Strip.Lines[0].StartPoint);
            // Generate the Texture of the Dot Shape
            _VertexTexture = _Generator.GeometryBorder(_CurrentVertex, _Brush);

            // Create the Cursor Shape
            _CursorShape = CustomTemplates.GetMouseCurser();
            // and the Texture with a small (pseudo anti-aliased) border
            Brush b = new Brush(1, Color.Black);
            b.AddMask(new BrushMaskFallOff(1));
            _CursorTexture = _Generator.GeometryFilledWithBorder(_CursorShape, new FillColor(Color.White), b);

        }
        #endregion

        #region Update

        public override void Update(float seconds,
                                GamePadState currentButtons, GamePadState previousButtons,
                                KeyboardState currentKeys, KeyboardState previousKeys,
                                MouseState currentMouse, MouseState previousMouse)
        {
            
            // update some position data
            _Cursor.Update(seconds, currentButtons, previousButtons, currentKeys, previousKeys, currentMouse, previousMouse);

            _LinePoint = _Strip.GetNearestPointOnEdge(_Cursor.Position);
            _CursorShape.Position = _Cursor.Position;
            _CurrentVertex.Position = _Strip.GetNearestCorner(_Cursor.Position);


            if (_IsMoveButtonDown)
            {
                _Strip.EditPoint(_MoveVertex, _Cursor.Position);
                _MoveVertex = _Cursor.Position;
            }
            #region Close to Poly / Clear
            // create poly or clear when C is released
            if ((currentKeys.IsKeyDown(Keys.C) && previousKeys.IsKeyUp(Keys.C))
                || (currentButtons.IsButtonDown(Buttons.Y) && previousButtons.IsButtonUp(Buttons.Y)))
            {
                // if the poly is not defined yet, we will define it
                if (_StripPoly == null)
                {
                    _StripPoly = new Polygon(_Strip);
                    MakeTexture();
                }
                // otherwise we reset all
                else
                {
                    // you don't really need to call Delete() 
                    // but if you do so, you can be sure that the GC will collect it soon.
                    _StripPoly.Dispose();
                    _StripPoly = null;

                    CreateInitialStrip();
                    MakeTexture();
                }
            }

            #endregion

            if ((currentKeys.IsKeyDown(Keys.D) && previousKeys.IsKeyUp(Keys.D))
                || (currentButtons.IsButtonDown(Buttons.RightTrigger) && previousButtons.IsButtonUp(Buttons.RightTrigger)))
            {
                _DisplayGeneratedTexture = !_DisplayGeneratedTexture;
                if(_DisplayGeneratedTexture)
                    MakeTexture();
            }
        }

        // called from CursorHelper class
        void _Cursor_ButtonPressed(TestGame.HelperClasses.CursorButton button)
        {
            // when the move button is pressed we save the vertex to move
            if (button == TestGame.HelperClasses.CursorButton.Middle)
            {
                _MoveVertex = _CurrentVertex.Position;

                _IsMoveButtonDown = true;
            }
        }
        // called from CursorHelper class
        void _Cursor_ButtonReleased(TestGame.HelperClasses.CursorButton button)
        {
            switch (button)
            {
                case TestGame.HelperClasses.CursorButton.Left: // ADD

                    _Strip.InsertPoint(_Cursor.Position, _Strip.GetNearestLine(_Cursor.Position));
                    MakeTexture();

                    break;
                case TestGame.HelperClasses.CursorButton.Right: // REMOVE

                    // an exception is thrown if the number of corners is less than 3
                    try // so we try to remove
                    {
                        _Strip.RemovePoint(_CurrentVertex.Position);
                        MakeTexture();
                    }
                    catch (TooFewPointsException) { } // and catch it if there are too few points

                    break;
                case TestGame.HelperClasses.CursorButton.Middle: // MOVE

                    // when the button is released, we apply the changes
                    _Strip.EditPoint(_MoveVertex, _Cursor.Position);
                    MakeTexture();
                    _IsMoveButtonDown = false;

                    break;
            }
        }



        #endregion

        #region Draw
        public override void Draw(SpriteBatch batch)
        {
            Game.GraphicsDevice.Clear(Color.DarkGoldenrod);

            batch.Begin();

            // pos is used to save the position of every Texture (except the cursor)
            // the Texture-Position is not the Shape Position because the Brush enlarges the texture a bit
            Vector2 pos = _Strip.Position - _Brush.GetTextureOffset();

            // draw the texture of the LineStrip / Polygon
            if(_DisplayGeneratedTexture)
                batch.Draw(_StripTexture, pos, Color.Yellow);


            if (_IsMoveButtonDown) // while moving a point
            {
                // draw the point to move at the cursor's position
                pos = _CursorShape.Position - _Brush.GetTextureOffset();
                batch.Draw(_VertexTexture, pos, Color.Red);

                // and another point at the corner which will be moved
                pos = _MoveVertex - _Brush.GetTextureOffset();
                batch.Draw(_VertexTexture, pos, Color.DarkOrange);
            }
            else // when not moving
            {
                // draw just the nearest corner
                pos = _CurrentVertex.Position - _Brush.GetTextureOffset();
                batch.Draw(_VertexTexture, pos, Color.Red);
            }

            // draw the cursor texture
            batch.Draw(_CursorTexture, _CursorShape.Position, Color.White);

            batch.End();

            _Generator.PrimitiveDrawer.DrawLineStrip(_Strip, Color.Yellow);

            Line l = _Strip.GetNearestLine(_LinePoint);
            Vector2 start = _Strip.Transform.TransformLocalToGlobal(l.StartPoint);
            Vector2 end = _Strip.Transform.TransformLocalToGlobal(l.EndPoint);
            _Generator.PrimitiveDrawer.DrawLine(new Line(_CursorShape.Position, start), Color.GreenYellow);
            _Generator.PrimitiveDrawer.DrawLine(new Line(_CursorShape.Position, end), Color.GreenYellow);
        }
        #endregion

        #region other methods
        // creates the LineStrip of the beginning (looks like a V)
        void CreateInitialStrip()
        {
            _Strip = new LineStrip(new Vector2(200, 200), new Vector2(300, 400), new Vector2(400, 200));
        }
        // generates the Texture for the LineStrip or Polygon (if the poly has been created)
        void MakeTexture()
        {
            if (!_DisplayGeneratedTexture)
                return;

            if (_StripPoly == null)
            {
                _StripTexture = _Generator.GeometryBorder(_Strip,  _Brush);
            }
            else
            {
                _StripTexture = _Generator.GeometryFilledWithBorder(_StripPoly, Color.Gray, _Brush);
            }
        }
        #endregion
    }
}
